﻿using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Update;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Data;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Channels;
using System.Threading.Tasks;
using System.Transactions;
using System.Xml.Linq;
using WCS.Infrastructure.Attr;
using WCS.Infrastructure.Dto.Request.Coupon;
using WCS.Infrastructure.Dto.Request.Lottery;
using WCS.Infrastructure.Dto.Request.User;
using WCS.Infrastructure.Dto.Response.Common;
using WCS.Infrastructure.Dto.Response.Coupon;
using WCS.Infrastructure.Dto.Response.Lottery;
using WCS.Infrastructure.Dto.Response.User;
using WCS.Infrastructure.EFCore;
using WCS.Infrastructure.IOC;
using WCS.Infrastructure.Tools;
using WCS.Service.Interface;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
using static System.Formats.Asn1.AsnWriter;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace WCS.Service.Instance
{
    [Inject]
    public class CouponService : BaseService, ICouponService
    {
        private readonly IConfiguration _configuration;
        private readonly CouponIOC _couponIOC;
        private readonly IUserService _userService;
        private readonly IHttpContextAccessor _httpContextAccessor;

        public CouponService(IConfiguration configuration, CouponIOC couponIOC, IUserService userService, IHttpContextAccessor httpContextAccessor)
        {
            _configuration = configuration;
            _couponIOC = couponIOC;
            _userService = userService;
            _httpContextAccessor = httpContextAccessor;
        }

        /// <summary>
        /// 新增优惠券模板
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> AddCouponTemplateAsync(CouponTemplateCreateRequestDto dto)
        {
            try
            {
                var permission = await _userService.GetCurUserRolePermissionAsync();
                if (permission == null || (permission.RoleType == 4 || permission.RoleType == 6))
                {
                    return GetResult(0, "无权限操作");
                }

                if (dto.ValidityType == 1 && dto.ValidityStartTime == null)
                {
                    return GetResult(0, "固定有效期的开始时间不能为空");
                }
                CouponTemplate CouponTemplate = new CouponTemplate()
                {
                    Id = Config.GUID(),
                    TemplateCode = Config.GenerateCode("CT"),
                    Name = dto.Name,
                    SchemeType = dto.SchemeType,
                    CouponType = dto.CouponType,
                    DiscountAmount = dto.DiscountAmount,
                    Discount = dto.Discount,
                    UseThresholdAmount = dto.UseThresholdAmount,
                    PlatformServiceFeeRate = dto.PlatformServiceFeeRate,
                    ReceiveStartTime = dto.ReceiveStartTime,
                    ReceiveEndTime = dto.ReceiveEndTime,
                    ValidityType = dto.ValidityType,
                    ValidityStartTime = dto.ValidityStartTime,
                    ValidityEndTime = dto.ValidityEndTime,
                    ValidityDuration = dto.ValidityDuration,
                    EffectiveDelayType = dto.EffectiveDelayType,
                    EffectiveDelayValue = dto.EffectiveDelayValue,
                    UseFrequencyType = dto.UseFrequencyType,
                    UseFrequencyLimit = dto.UseFrequencyLimit,
                    Total = dto.Total,
                    MaxIssuePerUser = dto.MaxIssuePerUser,
                    MerchantId = dto.MerchantId,
                    StoreId = dto.StoreId,
                    CodeId = dto.CodeId,
                    CreationTime = DateTime.Now,
                    IsDeleted = false,
                    EnableStatus = 1,
                    ExtraProperties = dto.ExtraProperties,
                    PosterUrl = dto.PosterUrl,
                    IsPush = dto.IsPush
                };

                await _couponIOC._couponTemplateEFCore.AddAsync(CouponTemplate);
                var result = await _couponIOC._couponTemplateEFCore.SaveChangesAsync();

                return GetResult(result, data: CouponTemplate.Id);
            }
            catch (Exception)
            {

                throw;
            }
        }

        /// <summary>
        /// 获取单个优惠券模板详情
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> GetCouponTemplateDetailAsync(string id)
        {
            try
            {
                // 先触发一次失效更新，保证统计为最新
                await ExpireCouponInstancesAsync(userIds: null, templateIds: new List<string> { id });

                // 一次性映射所有模板字段（Select）
                var dto = await _couponIOC._couponTemplateEFCore.QueryAll(d => d.Id == id)
                    .Select(d => new CouponTemplateDetailResponseDto
                    {
                        Id = d.Id,
                        TemplateCode = d.TemplateCode,
                        Name = d.Name,
                        SchemeType = d.SchemeType,
                        CouponType = d.CouponType,
                        DiscountAmount = d.DiscountAmount,
                        Discount = d.Discount,
                        UseThresholdAmount = d.UseThresholdAmount,
                        PlatformServiceFeeRate = d.PlatformServiceFeeRate,
                        ReceiveStartTime = d.ReceiveStartTime,
                        ReceiveEndTime = d.ReceiveEndTime,
                        ValidityType = d.ValidityType,
                        ValidityStartTime = d.ValidityStartTime,
                        ValidityEndTime = d.ValidityEndTime,
                        ValidityDuration = d.ValidityDuration,
                        EffectiveDelayType = d.EffectiveDelayType,
                        EffectiveDelayValue = d.EffectiveDelayValue,
                        UseFrequencyType = d.UseFrequencyType,
                        UseFrequencyLimit = d.UseFrequencyLimit,
                        Total = d.Total,
                        UsedCount = d.UsedCount,
                        MaxIssuePerUser = d.MaxIssuePerUser,
                        MerchantId = d.MerchantId,
                        StoreId = d.StoreId,
                        ExtraProperties = d.ExtraProperties,
                        ConcurrencyStamp = d.ConcurrencyStamp,
                        CreationTime = d.CreationTime,
                        CreatorId = d.CreatorId,
                        LastModificationTime = d.LastModificationTime,
                        LastModifierId = d.LastModifierId,
                        IsDeleted = d.IsDeleted,
                        DeleterId = d.DeleterId,
                        DeletionTime = d.DeletionTime,
                        EnableStatus = d.EnableStatus,
                        CodeId = d.CodeId,
                        PosterUrl = d.PosterUrl,
                        QuantityIssued = d.QuantityIssued,
                        ExpiredInstanceCount = 0, // 先占位，下面计算总失效数后再赋值
                        IsPush = d.IsPush
                    })
                    .FirstOrDefaultAsync();

                if (dto == null)
                {
                    return GetResult(message: "未找到该优惠券模板");
                }

                // 统计该模板对应的实例总共失效了多少（Status=3）
                dto.ExpiredInstanceCount = await _couponIOC._couponInstanceEFCore
                    .QueryAll(ci => ci.TemplateId == id && ci.Status == 3)
                    .CountAsync();

                return GetResult(1, data: dto);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 修改优惠券模板
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> UpdateCouponTemplateAsync(CouponTemplateCreateRequestDto dto)
        {
            try
            {
                var permission = await _userService.GetCurUserRolePermissionAsync();
                if (permission == null || (permission.RoleType == 4 || permission.RoleType == 6))
                {
                    return GetResult(0, "无权限操作");
                }

                var couponTemplate = await _couponIOC._couponTemplateEFCore.QueryAll(d => d.Id == dto.Id).FirstOrDefaultAsync();
                if (couponTemplate == null)
                {
                    return GetResult(0, "未找到该优惠券模板");
                }
                couponTemplate.Name = dto.Name;
                couponTemplate.SchemeType = dto.SchemeType;
                couponTemplate.CouponType = dto.CouponType;
                couponTemplate.DiscountAmount = dto.DiscountAmount;
                couponTemplate.Discount = dto.Discount;
                couponTemplate.UseThresholdAmount = dto.UseThresholdAmount;
                couponTemplate.PlatformServiceFeeRate = dto.PlatformServiceFeeRate;
                couponTemplate.Total = dto.Total;
                couponTemplate.MerchantId = dto.MerchantId;
                couponTemplate.StoreId = dto.StoreId;
                couponTemplate.CodeId = dto.CodeId;
                couponTemplate.PosterUrl = dto.PosterUrl;

                couponTemplate.ReceiveStartTime = dto.ReceiveStartTime;
                couponTemplate.ReceiveEndTime = dto.ReceiveEndTime;
                couponTemplate.ValidityType = dto.ValidityType;
                couponTemplate.ValidityStartTime = dto.ValidityStartTime;
                couponTemplate.ValidityEndTime = dto.ValidityEndTime;
                couponTemplate.ValidityDuration = dto.ValidityDuration;
                couponTemplate.EffectiveDelayType = dto.EffectiveDelayType;
                couponTemplate.EffectiveDelayValue = dto.EffectiveDelayValue;
                couponTemplate.UseFrequencyType = dto.UseFrequencyType;
                couponTemplate.UseFrequencyLimit = dto.UseFrequencyLimit;
                couponTemplate.MaxIssuePerUser = dto.MaxIssuePerUser;

                couponTemplate.EnableStatus = (int)dto.EnableStatus;
                couponTemplate.ExtraProperties = dto.ExtraProperties;
                couponTemplate.PosterUrl = dto.PosterUrl;
                couponTemplate.IsPush = dto.IsPush;

                await _couponIOC._couponTemplateEFCore.UpdateAsync(couponTemplate);
                var result = await _couponIOC._couponTemplateEFCore.SaveChangesAsync();
                return GetResult(result);
            }
            catch (Exception)
            {

                throw;
            }
        }

        /// <summary>
        /// 删除优惠券模板
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> DeleteCouponTemplateListAsync(string id)
        {
            try
            {
                var permission = await _userService.GetCurUserRolePermissionAsync();
                if (permission == null || (permission.RoleType == 4 || permission.RoleType == 6))
                {
                    return GetResult(0, "无权限操作");
                }

                var couponInstanc = await _couponIOC._couponInstanceEFCore.QueryAll(d => d.TemplateId == id).FirstOrDefaultAsync();
                if (couponInstanc != null)
                {
                    return GetResult(0, "该优惠券模板已被使用，无法删除");
                }
                var couponTemplate = await _couponIOC._couponTemplateEFCore.QueryAll(d => d.Id == id).FirstOrDefaultAsync();

                await _couponIOC._couponTemplateEFCore.DeleteAsync(couponTemplate);
                var result = await _couponIOC._couponTemplateEFCore.SaveChangesAsync();

                return GetResult(result);
            }
            catch (Exception)
            {

                throw;
            }
        }

        /// <summary>
        /// 优惠券模板增加库存
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> IncreaseCouponTemplateInventoryAsync(IncreaseCouponTemplateInventoryRequestDto dto)
        {
            if (string.IsNullOrWhiteSpace(dto.TemplateId))
                return GetResult(0, message: "模板ID不能为空");

            if (dto.IncreaseAmount <= 0)
                return GetResult(0, message: "增加数量必须大于0");

            var template = await _couponIOC._couponTemplateEFCore
                .QueryAll(d => d.Id == dto.TemplateId)
                .FirstOrDefaultAsync();

            if (template == null)
                return GetResult(0, message: "优惠券模板不存在");

            // 增加库存：Total += IncreaseAmount
            template.Total = template.Total + dto.IncreaseAmount;

            await _couponIOC._couponTemplateEFCore.UpdateAsync(template);
            var result = await _couponIOC._couponTemplateEFCore.SaveChangesAsync();

            return GetResult(result, data: new
            {
                TemplateId = template.Id,
                Total = template.Total,
                QuantityIssued = template.QuantityIssued,
                UsedCount = template.UsedCount
            });
        }

        /// <summary>
        /// 获取优惠券模板列表
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> GetCouponTemplateListAsync(CouponTemplateQueryRequestDto dto)
        {
            try
            {
                var permission = await _userService.GetCurUserRolePermissionAsync();
                List<string> storeIds = new List<string>();
                List<string> codeIds = new List<string>();
                if (permission != null)
                {
                    if (permission.RoleType == 4)//监事
                    {
                        return GetResult(0, "无权限查看");
                    }
                    if (permission.RoleType == 5)//店长
                    {
                        storeIds = permission.StoreIds;
                        if (storeIds.Count() < 1)
                            return GetResult(1, "无关联数据");
                    }
                    else if (permission.RoleType == 6)//收银员
                    {
                        codeIds = permission.CodeIds;
                        if (codeIds.Count() < 1)
                            return GetResult(1, "无关联数据");
                    }

                    if (storeIds.Count == 0 && !string.IsNullOrEmpty(dto.StoreId))
                        storeIds.Add(dto.StoreId);
                    if (codeIds.Count == 0 && !string.IsNullOrEmpty(dto.CodeId))
                        codeIds.Add(dto.CodeId);
                }

                var data = await _couponIOC._couponTemplateEFCore.QueryAll(out int total, dto.Page, dto.Limit,
                    d => (string.IsNullOrEmpty(dto.TemplateCode) || d.TemplateCode.Contains(dto.TemplateCode))
                    && (string.IsNullOrEmpty(dto.Name) || d.Name.Contains(dto.Name))
                    && (string.IsNullOrEmpty(dto.MerchantId) || d.MerchantId.Contains(dto.MerchantId))
                    && (!storeIds.Any() || storeIds.Contains(d.StoreId))
                    && (!codeIds.Any() || codeIds.Contains(d.CodeId)),
                    d => d.CreationTime, false).Select(d => new CouponTemplateQueryResponseDto
                    {
                        Id = d.Id,
                        TemplateCode = d.TemplateCode,
                        Name = d.Name,
                        CouponType = d.CouponType,
                        DiscountAmount = d.DiscountAmount ?? 0,
                        Discount = d.Discount ?? 0,
                        UseThresholdAmount = d.UseThresholdAmount ?? 0,
                        Total = d.Total,
                        UsedCount = d.UsedCount,
                        EnableStatus = d.EnableStatus,
                        PosterUrl = d.PosterUrl,
                        ReceiveStartTime = d.ReceiveStartTime,
                        ReceiveEndTime = d.ReceiveEndTime,
                        CreationTime = d.CreationTime,
                        ExtraProperties = d.ExtraProperties,
                        IsPush = d.IsPush
                    }).ToListAsync();

                return GetResult(1, data: new { total, data });
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 新增优惠券包
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> AddCouponPackageAsync(CouponPackageRequestDto dto)
        {
            try
            {
                if (dto.Ids.Count > 10)
                    return GetResult(message: "打包的优惠券不能超过十张");

                using var scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMinutes(2), TransactionScopeAsyncFlowOption.Enabled);

                string couponPackageId = Config.GUID();
                CouponPackage couponPackage = new CouponPackage()
                {
                    Id = couponPackageId,
                    PackageCode = Config.GenerateCode(),
                    Name = dto.Name,
                    Description = dto.Description,
                    QrcodeUrl = dto.QrcodeUrl,
                    MerchantId = dto.MerchantId,
                    CreationTime = DateTime.Now,
                    StartTime = (DateTime)dto.StartTime,
                    EndTime = (DateTime)dto.EndTime,
                    Status = (int)dto.Status,
                    IsDeleted = false,
                    EnableStatus = 1,
                    ExtraProperties = dto.ExtraProperties
                };

                await _couponIOC._couponPackageEFCore.AddAsync(couponPackage);
                var couponPackageResult = await _couponIOC._couponPackageEFCore.SaveChangesAsync();
                if (couponPackageResult == 0)
                {
                    return GetResult(message: "设置优惠券包失败，请稍后重试！");
                }

                if (dto.Ids.Count > 0)
                {
                    List<CouponPackageItem> couponPackageItems = new List<CouponPackageItem>();
                    foreach (var id in dto.Ids)
                    {
                        int index = 0;
                        CouponPackageItem couponPackageItem = new CouponPackageItem()
                        {
                            Id = Config.GUID(),
                            PackageId = couponPackageId,
                            TemplateId = id,
                            CreationTime = DateTime.Now,
                            Sort = index++,
                            Quantity = 1,
                        };
                        couponPackageItems.Add(couponPackageItem);
                    }

                    await _couponIOC._couponPackageItemEFCore.AddRangeAsync(couponPackageItems);
                    var couponPackageItemsResult = await _couponIOC._couponPackageItemEFCore.SaveChangesAsync();
                    if (couponPackageItemsResult == 0)
                    {
                        return GetResult(message: "设置优惠券包列表失败，请稍后重试！");
                    }
                }
                scope.Complete();

                return GetResult(1, data: couponPackageId);
            }
            catch (Exception)
            {
                throw;
            }


        }

        /// <summary>
        /// 删除优惠券包
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> DeleteCouponPackageAsync(CouponPackageRequestDto dto)
        {
            try
            {
                using var scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMinutes(2), TransactionScopeAsyncFlowOption.Enabled);

                var couponPackage = await _couponIOC._couponPackageEFCore.QueryAll(d => d.Id == dto.Id).SingleOrDefaultAsync();
                if (couponPackage == null)
                {
                    return GetResult(0, "未找到该优惠券包");
                }
                var couponPackageItems = await _couponIOC._couponPackageItemEFCore.QueryAll(d => d.PackageId == couponPackage.Id).ToListAsync();

                if (couponPackageItems.Count > 0)
                {
                    await _couponIOC._couponPackageItemEFCore.DeleteRangeAsync(couponPackageItems);
                    var couponPackageItemsResult = await _couponIOC._couponPackageItemEFCore.SaveChangesAsync();
                    if (couponPackageItemsResult == 0)
                    {
                        return GetResult(0, "删除优惠券包失败，请稍后重试！");
                    }
                }

                await _couponIOC._couponPackageEFCore.DeleteAsync(couponPackage);
                var couponPackageResult = await _couponIOC._couponPackageEFCore.SaveChangesAsync();
                if (couponPackageResult == 0)
                {
                    return GetResult(0, "删除优惠券包失败，请稍后重试！");
                }

                scope.Complete();
                return GetResult(couponPackageResult);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 获取单个优惠券包详情【Id】
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> GetCouponPackageAsync(CouponPackageRequestDto dto)
        {
            var couponPackage = await _couponIOC._couponPackageEFCore.QueryAll(d => d.Id == dto.Id).Select(d => new CouponPackageRequestDto
            {
                Id = d.Id,
                PackageCode = d.PackageCode,
                Name = d.Name,
                Description = d.Description,
                QrcodeUrl = d.QrcodeUrl,
                MerchantId = d.MerchantId,
                StartTime = d.StartTime,
                EndTime = d.EndTime,
                Status = d.Status,
                EnableStatus = d.EnableStatus,
                CreationTime = d.CreationTime,
                ExtraProperties = d.ExtraProperties
            }).SingleOrDefaultAsync();

            if (couponPackage == null)
            {
                return GetResult(0, "未找到该优惠券包");
            }

            var couponPackageItems = await _couponIOC._couponPackageItemEFCore.QueryAll(d => d.PackageId == couponPackage.Id,
                d => d.Sort, false).Select(d => new CouponPackageItemRequestDto
                {
                    Id = d.Id,
                    PackageId = d.PackageId,
                    TemplateId = d.TemplateId,
                    Quantity = d.Quantity,
                    Sort = d.Sort,
                    CreationTime = d.CreationTime
                }).ToListAsync();

            var CouponTemplateList = await _couponIOC._couponTemplateEFCore.QueryAll(d => couponPackageItems.Select(i => i.TemplateId).Contains(d.Id)).ToListAsync();

            foreach (var item in couponPackageItems)
            {
                item.TemplateCode = CouponTemplateList.FirstOrDefault(d => d.Id == item.TemplateId)?.TemplateCode;
                item.Name = CouponTemplateList.FirstOrDefault(d => d.Id == item.TemplateId)?.Name;
            }
            couponPackage.couponPackageItems = couponPackageItems;
            couponPackage.Ids = couponPackageItems.Select(d => d.TemplateId).ToList();
            return GetResult(1, data: couponPackage);
        }

        /// <summary>
        /// 获取优惠券包列表
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> GetCouponPackageListAsync(CouponPackageRequestDto dto)
        {
            var data = await _couponIOC._couponPackageEFCore.QueryAll(out int total, dto.Page, dto.Limit,
                d => (string.IsNullOrEmpty(dto.Key) || d.PackageCode.Contains(dto.Key))
                && (string.IsNullOrEmpty(dto.MerchantId) || d.MerchantId.Contains(dto.MerchantId)),
                d => d.CreationTime, false).ToListAsync();
            return GetResult(1, data: new { total, data });
        }

        /// <summary>
        /// 编辑优惠券包（仅使用 Ids 更新券列表，不使用 couponPackageItems，且不做排序处理）
        /// 功能：更新包基础信息，并将券包内的券模板集合与 dto.Ids 保持一致。
        /// - 若 dto.Ids 为空：清空券包内所有券项
        /// - 若 dto.Ids 非空：仅保留这些模板，新增缺失模板（Quantity=1，Sort=0），删除其它模板
        /// - 不调整已存在券项的 Sort 和 Quantity（除新增外）
        /// </summary>
        /// <param name="dto">
        /// 请求参数：
        /// - 必填：Id（券包ID）
        /// - 可选：Name、Description、QrcodeUrl、StartTime、EndTime、Status、EnableStatus、ExtraProperties
        /// - 仅使用：Ids（模板ID集合，最多10个）
        /// </param>
        /// <returns>
        /// ApiResult：
        /// - code=1：成功，data 包含 { PackageId, RemovedCount, AddedCount }
        /// - code=0：失败并附带错误消息
        /// </returns>
        /// <exception cref="Exception">数据库访问或事务执行过程中发生异常时抛出</exception>
        public async Task<ApiResult> UpdateCouponPackageAsync(CouponPackageRequestDto dto)
        {
            try
            {
                using var scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMinutes(2), TransactionScopeAsyncFlowOption.Enabled);

                // 1) 查询并校验包主体
                var couponPackage = await _couponIOC._couponPackageEFCore.QueryAll(d => d.Id == dto.Id).SingleOrDefaultAsync();
                if (couponPackage == null)
                {
                    return GetResult(0, "未找到该优惠券包");
                }

                // 2) 更新包基础信息（如未传入，可保持原值，视你的 DTO 默认值策略）
                couponPackage.Name = dto.Name;
                couponPackage.Description = dto.Description;
                couponPackage.QrcodeUrl = dto.QrcodeUrl;
                couponPackage.Status = (int)dto.Status!;
                couponPackage.StartTime = (DateTime)dto.StartTime;
                couponPackage.EndTime = (DateTime)dto.EndTime!;
                couponPackage.EnableStatus = (int)dto.EnableStatus!;
                couponPackage.ExtraProperties = dto.ExtraProperties;

                await _couponIOC._couponPackageEFCore.UpdateAsync(couponPackage);
                var couponPackageResult = await _couponIOC._couponPackageEFCore.SaveChangesAsync();
                if (couponPackageResult == 0)
                {
                    return GetResult(message: "更新优惠券包失败，请稍后重试！");
                }

                // 3) 仅使用 Ids 作为目标模板集合（不使用 couponPackageItems，不做排序）
                var targetIds = (dto.Ids ?? new List<string>())
                    .Where(x => !string.IsNullOrWhiteSpace(x))
                    .Distinct()
                    .ToList();

                if (targetIds.Count > 10)
                {
                    return GetResult(0, "打包的优惠券不能超过十张");
                }

                // 4) 查询现有券包项
                var existingItems = await _couponIOC._couponPackageItemEFCore
                    .QueryAll(d => d.PackageId == dto.Id!)
                    .ToListAsync();

                var existingIds = existingItems.Select(x => x.TemplateId).ToHashSet();

                // 需要删除的：现有有但目标没有
                var toRemove = existingItems
                    .Where(x => !targetIds.Contains(x.TemplateId))
                    .ToList();

                // 需要新增的：目标有但现有没有（Quantity=1，Sort=0；不做排序逻辑）
                var toAdd = targetIds
                    .Where(tid => !existingIds.Contains(tid))
                    .Select(tid => new CouponPackageItem
                    {
                        Id = Config.GUID(),
                        PackageId = dto.Id!,
                        TemplateId = tid,
                        CreationTime = DateTime.Now,
                        Quantity = 1,
                        Sort = 0 // 不做排序：统一置 0；保留既有项的 Sort 不变
                    })
                    .ToList();

                // 无需更新项（不调整既有项的 Quantity/Sort）

                // 5) 执行删除
                if (toRemove.Count > 0)
                {
                    await _couponIOC._couponPackageItemEFCore.DeleteRangeAsync(toRemove);
                    var removeResult = await _couponIOC._couponPackageItemEFCore.SaveChangesAsync();
                    if (removeResult == 0)
                    {
                        return GetResult(0, "更新券列表失败：删除旧券项失败");
                    }
                }

                // 6) 执行新增
                if (toAdd.Count > 0)
                {
                    await _couponIOC._couponPackageItemEFCore.AddRangeAsync(toAdd);
                    var addResult = await _couponIOC._couponPackageItemEFCore.SaveChangesAsync();
                    if (addResult == 0)
                    {
                        return GetResult(0, "更新券列表失败：新增新券项失败");
                    }
                }

                scope.Complete();
                return GetResult(1, data: new
                {
                    PackageId = dto.Id,
                    RemovedCount = toRemove.Count,
                    AddedCount = toAdd.Count
                });
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 领取优惠券包【PackageId】
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> ReceiveCouponPackageAsync(CouponPackageReceiveRecordRequestDto dto)
        {
            try
            {
                using var scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMinutes(2), TransactionScopeAsyncFlowOption.Enabled);

                var couponPackage = await _couponIOC._couponPackageEFCore.QueryAll(d => d.Id == dto.PackageId).FirstOrDefaultAsync();
                if (couponPackage == null)
                {
                    return GetResult(0, "未找到该优惠券包");
                }
                if (couponPackage.EnableStatus != 1)
                {
                    return GetResult(0, "该优惠券包未启用或已过期");
                }
                if (couponPackage.StartTime > DateTime.Now || couponPackage.EndTime < DateTime.Now)
                {
                    return GetResult(0, "该优惠券包未在有效期内，无法领取");
                }
                if (couponPackage.Status == 1) // 草稿状态
                {
                    return GetResult(0, "该优惠券包未发布，无法领取");
                }
                if (couponPackage.Status == 3) // 已停用状态
                {
                    return GetResult(0, "该优惠券包已停用，无法领取");
                }

                CouponPackageReceiveRecord couponPackageReceiveRecord = new CouponPackageReceiveRecord()
                {
                    Id = Config.GUID(),
                    PackageId = couponPackage.Id,
                    UserId = _httpContextAccessor.HttpContext?.GetCurrentUserId(),
                    CreationTime = DateTime.Now,
                    ReceiveChannel = (int)dto.ReceiveChannel != null ? (int)dto.ReceiveChannel : 2,
                    ReceiveCode = dto.ReceiveCode,
                    DeviceInfo = dto.DeviceInfo,
                    Ipaddress = Config.GetIp(),
                };

                await _couponIOC._couponPackageReceiveRecordEFCore.AddAsync(couponPackageReceiveRecord);
                var couponPackageReceiveRecordResult = await _couponIOC._couponPackageReceiveRecordEFCore.SaveChangesAsync();

                var couponPackageItems = await _couponIOC._couponPackageItemEFCore.QueryAll(d => d.PackageId == couponPackage.Id).ToListAsync();

                if (couponPackageItems.Count == 0)
                {
                    return GetResult(0, "该优惠券包没有可领取的优惠券");
                }


                var couponInstanceResult = await UserGetCouponAsync(new CouponInstanceCreateRequestDto
                {
                    TemplateIds = couponPackageItems.Select(d => d.TemplateId).ToList()
                });

                if (couponPackageReceiveRecordResult == 0 || couponInstanceResult.code == ApiCode.fail)
                {
                    return GetResult(0, "领取优惠券包失败，请稍后重试！");
                }

                scope.Complete();
                return GetResult(1);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 用户领取优惠券
        /// 功能说明：
        /// 1. 校验当前用户及其 UnionId 关联用户的领取资格与模板库存
        /// 2. 校验模板启用状态与用户领取时间窗口（ReceiveStartTime <= 领取时间 <= ReceiveEndTime）
        /// 3. 计算优惠券实例的可用开始/结束时间（延迟生效 + 有效期类型组合）
        /// 4. 批量写入实例后，按模板进行“数据库原子更新”递增 QuantityIssued，防止并发超发：
        ///    - 使用 ExecuteUpdateAsync 在数据库端执行 SET QuantityIssued = COALESCE(QuantityIssued,0) + increment
        ///    - 在 WHERE 条件加入 COALESCE(QuantityIssued,0) + increment <= Total，保证不会超过总量
        /// 5. 若某个模板的原子更新受影响行数为 0，判定为并发导致库存不足，直接返回错误（不提交事务，自动回滚）
        /// 参数说明：
        /// - dto：券实例创建请求参数，包含 TemplateIds、RecommendMerchantId 等
        /// 返回值说明：
        /// - ApiResult：包含成功发放的实例数量等信息
        /// 异常说明：
        /// - 可能抛出数据库写入异常、事务异常等，外层统一捕获后上抛
        /// </summary>
        /// <param name="dto">券实例创建请求参数，包含 TemplateIds、RecommendMerchantId 等</param>
        /// <returns>ApiResult：成功数量与提示信息</returns>
        /// <exception cref="Exception">数据库异常或事务异常</exception>
        public async Task<ApiResult> UserGetCouponAsync(CouponInstanceCreateRequestDto dto)
        {
            try
            {
                using var scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMinutes(2), TransactionScopeAsyncFlowOption.Enabled);
                //var txOpts = new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.Serializable, Timeout = TimeSpan.FromMinutes(2) };
                //using var scope = new TransactionScope(TransactionScopeOption.Required, txOpts, TransactionScopeAsyncFlowOption.Enabled);

                // 获取当前登录用户
                string userId = _httpContextAccessor.HttpContext?.GetCurrentUserId() ?? string.Empty;
                if (string.IsNullOrEmpty(userId))
                {
                    return GetResult(message: "获取不到用户信息，请重新登录");
                }

                // 查询用户小程序信息，用于基于 UnionId 合并用户
                var userMiniProgram = await _couponIOC._sysUserMiniProgramEFCore.QueryAll(d => d.Id == userId).FirstOrDefaultAsync();
                if (userMiniProgram == null)
                {
                    return GetResult(message: "获取不到用户信息，请重新登录");
                }

                // 基于 UnionId 获取属于同一用户的所有小程序用户ID
                List<string> relatedUserIds = new List<string>();
                if (!string.IsNullOrEmpty(userMiniProgram.UnionId))
                {
                    relatedUserIds = await _couponIOC._sysUserMiniProgramEFCore
                        .QueryAll(d => d.UnionId == userMiniProgram.UnionId)
                        .Select(d => d.Id)
                        .ToListAsync();
                }
                else
                {
                    relatedUserIds.Add(userId);
                }

                //触发一次失效更新，保证统计为最新
                await ExpireCouponInstancesAsync(userIds: null, templateIds: dto.TemplateIds);

                List<CouponInstance> couponInstances = new List<CouponInstance>();
                DateTime receiveTime = DateTime.Now; // 领取时间（实例创建时间）

                foreach (var templateId in dto.TemplateIds)
                {
                    var couponTemplate = await _couponIOC._couponTemplateEFCore.QueryAll(d => d.Id == templateId).FirstOrDefaultAsync();
                    if (couponTemplate == null)
                    {
                        continue; // 模板不存在则跳过
                    }

                    // 模板启用状态校验
                    if (couponTemplate.EnableStatus != 1)
                    {
                        continue; // 模板未启用，跳过
                    }

                    // 领取时间窗口校验（ReceiveStartTime <= 当前时间 <= ReceiveEndTime）
                    if (receiveTime < couponTemplate.ReceiveStartTime || receiveTime > couponTemplate.ReceiveEndTime)
                    {
                        continue; // 当前不在可领取时间范围内
                    }

                    // 库存校验：发放数量达到总数后不可领取（初步校验，最终以数据库原子更新为准）
                    //var issued = couponTemplate.QuantityIssued ?? 0;
                    //if (issued >= couponTemplate.Total)
                    //{
                    //    continue;
                    //}

                    //库存校验:可领取数量=总库存-已发放数量+已失效数量
                    var expiredCount = await _couponIOC._couponInstanceEFCore
                        .QueryAll(d => d.TemplateId == templateId && d.Status == 3)
                        .CountAsync();
                    var issued = couponTemplate.Total - couponTemplate.QuantityIssued + expiredCount;
                    if (issued <= 0)
                    {
                        continue;
                    }

                    // 每人最多领取数量（基于 UnionId 识别同一用户）
                    var alreadyReceivedCount = await _couponIOC._couponInstanceEFCore
                        .QueryAll(d => relatedUserIds.Contains(d.UserId) && d.TemplateId == templateId)
                        .CountAsync();

                    if (alreadyReceivedCount >= couponTemplate.MaxIssuePerUser)
                    {
                        continue;
                    }

                    // 计算延迟生效的基础开始时间 baseStart
                    DateTime baseStart;
                    switch (couponTemplate.EffectiveDelayType)
                    {
                        case 0: // 领取后立即生效
                            baseStart = receiveTime;
                            break;
                        case 1: // 次日0点生效
                            baseStart = receiveTime.Date.AddDays(1);
                            break;
                        case 2: // 领取后N小时生效
                            if (!couponTemplate.EffectiveDelayValue.HasValue || couponTemplate.EffectiveDelayValue.Value <= 0)
                            {
                                // 模板配置错误（延迟值无效），跳过该优惠券
                                continue;
                            }
                            baseStart = receiveTime.AddHours(couponTemplate.EffectiveDelayValue.Value);
                            break;
                        default:
                            // 未知生效类型，默认立即生效
                            baseStart = receiveTime;
                            break;
                    }

                    // 组合有效期类型与延迟生效，计算 AvailableStartTime/AvailableEndTime
                    DateTime availableStartTime;
                    DateTime availableEndTime;

                    switch (couponTemplate.ValidityType)
                    {
                        case 1: // 固定时间范围
                            if (!couponTemplate.ValidityStartTime.HasValue || !couponTemplate.ValidityEndTime.HasValue)
                            {
                                // 模板配置错误（固定有效期但缺少开始/结束时间），跳过该优惠券
                                continue;
                            }
                            // 开始时间为 Max(延迟生效时间, 固定有效期开始时间)
                            availableStartTime = baseStart >= couponTemplate.ValidityStartTime.Value
                                ? baseStart
                                : couponTemplate.ValidityStartTime.Value;

                            // 结束时间为固定有效期结束时间
                            availableEndTime = couponTemplate.ValidityEndTime.Value;
                            break;

                        case 2: // 领取后N小时内可用（相对时长）
                            if (!couponTemplate.ValidityDuration.HasValue || couponTemplate.ValidityDuration.Value <= 0)
                            {
                                // 模板配置错误（有效期时长无效），跳过该优惠券
                                continue;
                            }
                            // 有效期基于实际可用开始时间计算
                            availableStartTime = baseStart;
                            availableEndTime = baseStart.AddHours(couponTemplate.ValidityDuration.Value);
                            break;

                        default:
                            // 未知有效期类型，无法计算，跳过该优惠券
                            continue;
                    }

                    // 防御：开始时间不能晚于或等于结束时间
                    if (availableStartTime >= availableEndTime)
                    {
                        continue;
                    }

                    // 创建优惠券实例并赋值时间与归属信息
                    CouponInstance couponInstance = new CouponInstance()
                    {
                        Id = Config.GUID(),
                        InstanceCode = Config.GenerateCode("CI"),
                        TemplateId = templateId,
                        UserId = userId,
                        UnionId = userMiniProgram.UnionId, // 记录 UnionId，便于后续聚合
                        CreationTime = receiveTime,
                        AvailableStartTime = availableStartTime,
                        AvailableEndTime = availableEndTime,
                        RecommendMerchantId = dto.RecommendMerchantId,
                        MerchantId = couponTemplate.MerchantId,
                        StoreId = couponTemplate.StoreId,
                        CodeId = couponTemplate.CodeId,
                        Status = string.IsNullOrEmpty(dto.ActivityId) ? 1 : 0, // 初始状态为未使用
                        ExtraProperties = couponTemplate.ExtraProperties,

                        ActivityId = dto.ActivityId,
                        ActivationExpirationTime = dto.ActivationExpirationTime == null ? availableEndTime : dto.ActivationExpirationTime
                    };

                    couponInstances.Add(couponInstance);
                }

                // 若有可发放的券，批量写入
                if (couponInstances.Count > 0)
                {
                    await _couponIOC._couponInstanceEFCore.AddRangeAsync(couponInstances);
                    var couponInstanceResult = await _couponIOC._couponInstanceEFCore.SaveChangesAsync();

                    if (couponInstanceResult == 0)
                    {
                        return GetResult(0, "领取优惠券失败，请稍后重试！");
                    }

                    // 成功发券后，按模板分组准备增量信息
                    var issueGroups = couponInstances
                        .GroupBy(ci => ci.TemplateId)
                        .Select(g => new { TemplateId = g.Key, Count = g.Count() })
                        .ToList();

                    // 使用“数据库原子更新”递增 QuantityIssued，防止并发超发
                    // 每个模板单独执行 UPDATE，并在 WHERE 条件中限制不超过 Total
                    foreach (var ig in issueGroups)
                    {
                        //var affectedRows = await _couponIOC._couponTemplateEFCore
                        //    .QueryAll(t => t.Id == ig.TemplateId && ((t.QuantityIssued ?? 0) + ig.Count) <= t.Total)
                        //    .ExecuteUpdateAsync(setters => setters
                        //        .SetProperty(t => t.QuantityIssued, t => (t.QuantityIssued ?? 0) + ig.Count));

                        var failureCount = await _couponIOC._couponInstanceEFCore.QueryAll(ci => ci.TemplateId == ig.TemplateId && ci.Status == 3).CountAsync();

                        var affectedRows = await _couponIOC._couponTemplateEFCore
                           .QueryAll(t => t.Id == ig.TemplateId && (((t.QuantityIssued ?? 0)
                           - failureCount)
                           + ig.Count) <= t.Total)
                            .ExecuteUpdateAsync(setters => setters
                                .SetProperty(t => t.QuantityIssued, t => (t.QuantityIssued ?? 0) + ig.Count));

                        if (affectedRows == 0)
                        {
                            // 若某个模板更新失败（行数为0），说明与并发竞争后库存不足
                            // 为保证一致性，不提交事务（scope 未 Complete），直接返回错误
                            return GetResult(0, "库存不足或并发超发，领取失败，请稍后重试");
                        }
                    }

                    // 上述所有模板的原子更新均成功，提交事务
                    scope.Complete();
                    return GetResult(couponInstanceResult, data: new GetCouponsResponseDto()
                    {
                        Count = couponInstanceResult,
                        Ids = couponInstances.Select(d => d.Id).ToList()
                    });
                }
                else
                {
                    // 没有任何可发放的券
                    return GetResult(0, "暂无可领取的优惠券或不满足领取条件");
                }
            }
            catch (Exception)
            {
                // 统一上抛，由上层中间件处理日志与返回
                throw;
            }
        }

        /// <summary>
        /// 确认核销（支付成功回调时调用）
        /// </summary>
        /// <param name="couponInstanceId">优惠券实例Id</param>
        /// <param name="orderId">订单Id</param>
        /// <returns>ApiResult: 成功返回 code=1；失败返回 code=0 并附带错误消息</returns>
        /// <exception cref="Exception">数据库访问异常时抛出</exception>
        public async Task<ApiResult> ConfirmCouponConsumptionAsync(string couponInstanceId, string orderId)
        {
            try
            {
                var couponInstance = await _couponIOC._couponInstanceEFCore
                    .QueryAll(d => d.Id == couponInstanceId)
                    .FirstOrDefaultAsync();

                if (couponInstance == null)
                {
                    return GetResult(0, "未找到优惠券实例");
                }

                // 幂等：已使用则直接返回成功
                if (couponInstance.Status == 2)
                {
                    return GetResult(1, "该优惠券已确认核销");
                }

                // 必须存在当前订单对应、且未取消的占用记录
                var occupyRecord = await _couponIOC._couponConsumptionRecordEFCore
                    .QueryAll(r => r.CouponInstanceId == couponInstanceId
                                   && r.OrderId == orderId
                                   && !r.IsCanceled)
                    .FirstOrDefaultAsync();

                if (occupyRecord == null)
                {
                    return GetResult(0, "未找到本订单对应的核销占用记录或已被取消");
                }

                // 更新优惠券实例状态为已使用
                couponInstance.Status = 2;
                await _couponIOC._couponInstanceEFCore.UpdateAsync(couponInstance);

                // 模板 UsedCount += 1
                var template = await _couponIOC._couponTemplateEFCore
                    .QueryAll(d => d.Id == couponInstance.TemplateId)
                    .FirstOrDefaultAsync();

                if (template != null)
                {
                    template.UsedCount = template.UsedCount + 1;
                    await _couponIOC._couponTemplateEFCore.UpdateAsync(template);
                }

                // 持久化
                await _couponIOC._couponInstanceEFCore.SaveChangesAsync();
                if (template != null)
                {
                    await _couponIOC._couponTemplateEFCore.SaveChangesAsync();
                }

                return GetResult(1, "核销确认成功");
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 撤销核销（支付失败/取消/超时等场景调用）
        /// </summary>
        /// <param name="couponInstanceId">优惠券实例Id</param>
        /// <param name="orderId">订单Id</param>
        /// <returns>ApiResult: 成功返回 code=1；失败返回 code=0 并附带错误消息</returns>
        /// <exception cref="Exception">数据库访问异常时抛出</exception>
        public async Task<ApiResult> CancelCouponConsumptionAsync(string couponInstanceId, string orderId)
        {
            try
            {
                // 找到占用记录并标记为取消
                var occupyRecord = await _couponIOC._couponConsumptionRecordEFCore
                    .QueryAll(r => r.CouponInstanceId == couponInstanceId
                                   && r.OrderId == orderId
                                   && !r.IsCanceled)
                    .FirstOrDefaultAsync();

                if (occupyRecord != null)
                {
                    occupyRecord.IsCanceled = true;
                    await _couponIOC._couponConsumptionRecordEFCore.UpdateAsync(occupyRecord);
                    await _couponIOC._couponConsumptionRecordEFCore.SaveChangesAsync();
                }

                // 如果优惠券未被确认为已使用（Status != 2），则无需其它操作（保持或回到未使用）
                // 若你的“下单时不置已使用”的策略已生效，这里通常不需要回滚实例状态。
                return GetResult(1, "核销占用已撤销");
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 礼品券核销
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> RedeemGiftCouponAsync(GiftCouponRedeemRequestDto dto)
        {
            try
            {
                using var scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMinutes(2), TransactionScopeAsyncFlowOption.Enabled);

                var couponInstance = await _couponIOC._couponInstanceEFCore.QueryAll(d => d.Id == dto.CouponInstanceId).FirstOrDefaultAsync();
                if (couponInstance == null)
                {
                    return GetResult(0, "优惠券实例不存在");
                }
                if (couponInstance.Status != 1)
                {
                    return GetResult(0, "优惠券状态不正确，无法核销"); // 1=未使用，2=已使用，3=已过期
                }
                if (couponInstance.AvailableEndTime < DateTime.Now)
                {
                    return GetResult(0, "优惠券已过期，无法核销");
                }

                var couponTemplate = await _couponIOC._couponTemplateEFCore.QueryAll(d => d.Id == couponInstance.TemplateId).FirstOrDefaultAsync();
                if (couponTemplate == null)
                {
                    return GetResult(0, "优惠券模板不存在");
                }
                if (couponTemplate.CouponType != 3) // 3=礼品券
                {
                    return GetResult(0, "该优惠券不是礼品券，无法使用礼品核销接口");
                }

                // 写核销记录（礼品券不关联订单）
                var record = new CouponConsumptionRecord
                {
                    Id = Config.GUID(),
                    CouponInstanceId = couponInstance.Id,
                    OrderId = "",
                    UserId = couponInstance.UserId, // 记录券归属用户
                    ConsumptionTime = DateTime.Now,
                    ConsumptionType = 4, // 4=API接口核销
                    ConsumptionCodeId = couponInstance.CodeId,
                    ConsumptionIp = Config.GetIp(),
                    IsCanceled = false,
                    // 可在后续扩展 Remark/ExtraProperties，如需
                };

                await _couponIOC._couponConsumptionRecordEFCore.AddAsync(record);
                var saveRecord = await _couponIOC._couponConsumptionRecordEFCore.SaveChangesAsync();

                couponInstance.Status = 2; // 置为已使用
                await _couponIOC._couponInstanceEFCore.UpdateAsync(couponInstance);
                var saveInstance = await _couponIOC._couponInstanceEFCore.SaveChangesAsync();

                // 累加模板已使用数量
                couponTemplate.UsedCount = couponTemplate.UsedCount + 1;
                await _couponIOC._couponTemplateEFCore.UpdateAsync(couponTemplate);
                var saveTemplate = await _couponIOC._couponTemplateEFCore.SaveChangesAsync();

                if (saveRecord == 0 || saveInstance == 0 || saveTemplate == 0)
                {
                    return GetResult(0, "礼品券核销失败，请稍后重试");
                }

                scope.Complete();
                return GetResult(1, "礼品券核销成功");
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 计算优惠券实例
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> CalculateCouponInstanceAsync(CalculateCouponInstanceRequestDto dto)
        {
            var couponInstance = await _couponIOC._couponInstanceEFCore.QueryAll(d => d.Id == dto.CouponInstanceId).SingleOrDefaultAsync();
            var couponTemplate = await _couponIOC._couponTemplateEFCore.QueryAll(d => d.Id == couponInstance.TemplateId).SingleOrDefaultAsync();
            dto.PlatformServiceFeeRate = (decimal)couponTemplate.PlatformServiceFeeRate;
            if (couponTemplate.SchemeType == 1)//消费者承担服务费 A
            {
                if (couponTemplate.CouponType == 2)//折扣券
                {
                    dto.DiscountAmount = (long)(dto.OrderAmount * couponTemplate.Discount);
                    dto.PayAmount = (long)(dto.OrderAmount - dto.DiscountAmount);
                    dto.PlatformServiceFee = (long)Math.Ceiling((decimal)(dto.OrderAmount * couponTemplate.PlatformServiceFeeRate));//从订单金额计算平台服务费
                }
            }
            if (couponTemplate.SchemeType == 2)//商家承担服务费 B
            {
                if (couponTemplate.CouponType == 1)//减免券
                {
                    dto.DiscountAmount = (long)couponTemplate.DiscountAmount;
                    dto.PayAmount = (long)(dto.OrderAmount - couponTemplate.DiscountAmount);
                    dto.PlatformServiceFee = (long)Math.Ceiling((decimal)(dto.PayAmount * couponTemplate.PlatformServiceFeeRate));//从实收金额计算平台服务费
                }
                if (couponTemplate.CouponType == 2)//折扣券
                {
                    dto.DiscountAmount = (long)(dto.OrderAmount * couponTemplate.Discount);
                    dto.PayAmount = (long)(dto.OrderAmount - dto.DiscountAmount);
                    dto.PlatformServiceFee = (long)Math.Ceiling((decimal)(dto.PayAmount * couponTemplate.PlatformServiceFeeRate));///从实收金额计算平台服务费
                }
            }
            dto.RecommendMerchantId = couponInstance.RecommendMerchantId;
            if (!string.IsNullOrEmpty(dto.RecommendMerchantId))
                dto.RecommendationCommissionAmount = (long)(dto.PlatformServiceFee * 0.05m); // 推荐商家佣金为平台服务费的5%
            return GetResult(1, data: dto);
        }

        /// <summary>
        /// 平台随机推送优惠券
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> PlatformRandomlyPusheCouponAsync(CouponTemplateQueryRequestDto dto)
        {
            var currentTime = DateTime.Now;
            var couponTemplate = await _couponIOC._couponTemplateEFCore.QueryAll(/*out int total, dto.Page, dto.Limit,*/
                d => (string.IsNullOrEmpty(dto.MerchantId) || d.MerchantId.Contains(dto.MerchantId))
            && (string.IsNullOrEmpty(dto.StoreId) || d.StoreId.Contains(dto.StoreId))
            && d.EnableStatus == 1
            && d.CouponType != 3 // 排除礼品券
            && d.IsPush == true// 仅推送类型
            && (d.ReceiveStartTime < currentTime && d.ReceiveEndTime > currentTime),
            d => d.CreationTime, false).Select(d => new CouponTemplateQueryResponseDto
            {
                Id = d.Id,
                TemplateCode = d.TemplateCode,
                Name = d.Name,
                CouponType = d.CouponType,
                DiscountAmount = d.DiscountAmount,
                Discount = d.Discount,
                UseThresholdAmount = d.UseThresholdAmount,
                ExtraProperties = d.ExtraProperties
            }).ToListAsync();

            var randomCoupons = new List<CouponTemplateQueryResponseDto>();
            if (string.IsNullOrEmpty(dto.StoreId))
                randomCoupons = couponTemplate.OrderBy(x => Guid.NewGuid()).Take(dto.Limit).ToList();
            else
                randomCoupons = couponTemplate;

            return GetResult(1, data: couponTemplate);
        }

        /// <summary>
        /// 获取用户优惠券实例列表
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> GetUserCouponInstanceListAsync(CouponInstanceQueryRequestDto dto)
        {
            try
            {
                string userId = _httpContextAccessor.HttpContext?.GetCurrentUserId() ?? string.Empty;
                if (string.IsNullOrEmpty(userId))
                {
                    return GetResult(message: "获取不到用户信息，请重新登录");
                }
                var userMiniProgram = await _couponIOC._sysUserMiniProgramEFCore.QueryAll(d => d.Id == userId).FirstOrDefaultAsync();
                if (userMiniProgram == null)
                {
                    return GetResult(message: "获取不到用户信息，请重新登录");
                }

                var userMiniPrograms = await _couponIOC._sysUserMiniProgramEFCore.QueryAll(d => d.UnionId == userMiniProgram.UnionId && !string.IsNullOrEmpty(userMiniProgram.UnionId)).ToListAsync();

                List<string> userIds = new List<string>();
                if (userMiniPrograms.Count > 0)
                {
                    userIds = userMiniPrograms.Select(d => d.Id).ToList();
                }
                else
                {
                    userIds.Add(userId);
                }

                // 过期处理
                await ExpireCouponInstancesAsync(userIds: userIds, templateIds: null);

                var data = await _couponIOC._couponInstanceEFCore.QueryAll(out int total, dto.Page, dto.Limit,
                    d => userIds.Contains(d.UserId)
                    && (string.IsNullOrEmpty(dto.MerchantId) || d.MerchantId.Contains(dto.MerchantId))
                    && (string.IsNullOrEmpty(dto.StoreId) || d.StoreId.Contains(dto.StoreId))
                    && d.Status == dto.Status,
                    d => d.CreationTime, false).Select(d => new CouponInstanceQueryResponseDto
                    {
                        Id = d.Id,
                        InstanceCode = d.InstanceCode,
                        TemplateId = d.TemplateId,
                        UserId = d.UserId,
                        AvailableStartTime = d.AvailableStartTime,
                        AvailableEndTime = d.AvailableEndTime,
                        CreationTime = d.CreationTime,
                        Status = d.Status,
                    }).ToListAsync();

                //var data = coupons.Where(d => d.Status == dto.Status).Select(d => new CouponInstanceQueryResponseDto
                //{
                //    Id = d.Id,
                //    InstanceCode = d.InstanceCode,
                //    TemplateId = d.TemplateId,
                //    UserId = d.UserId,
                //    AvailableStartTime = d.AvailableStartTime,
                //    AvailableEndTime = d.AvailableEndTime,
                //    CreationTime = d.CreationTime,
                //    Status = d.Status,
                //}).ToList();

                var couponTemplateList = await _couponIOC._couponTemplateEFCore.QueryAll(d => data.Select(i => i.TemplateId).Contains(d.Id)).ToListAsync();
                foreach (var couponInstance in data)
                {
                    var template = couponTemplateList.FirstOrDefault(i => i.Id == couponInstance.TemplateId);
                    if (template == null)
                        continue; // 找不到模板，跳过当前项

                    couponInstance.TemplateName = template.Name;
                    couponInstance.CouponType = template.CouponType;
                    couponInstance.DiscountAmount = template.DiscountAmount;
                    couponInstance.Discount = template.Discount;
                    couponInstance.UseThresholdAmount = template.UseThresholdAmount;
                    couponInstance.PlatformServiceFeeRate = template.PlatformServiceFeeRate;
                    couponInstance.SchemeType = template.SchemeType;
                    couponInstance.StoreId = template.StoreId;
                    couponInstance.StoreName = await _couponIOC._storeEFCore.QueryAll(d => d.Id == template.StoreId).Select(d => d.Name).FirstOrDefaultAsync();
                    couponInstance.MerchantId = template.MerchantId;
                    couponInstance.MerchantName = await _couponIOC._merchantEFCore.QueryAll(d => d.Id == template.MerchantId).Select(d => d.Name).FirstOrDefaultAsync();
                    couponInstance.ExtraProperties = template.ExtraProperties;
                }

                //data = data.Where(d => (string.IsNullOrEmpty(dto.MerchantId) || d.MerchantId.Contains(dto.MerchantId))
                //&& (string.IsNullOrEmpty(dto.StoreId) || d.StoreId.Contains(dto.StoreId))).ToList();

                return GetResult(1, data: new
                {
                    total,//total = data.Count,
                    data
                });
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 新增优惠券核销记录（下单阶段-占用锁定，不直接将实例置为已使用）
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> AddCouponConsumptionRecordAsync(CouponConsumptionRecordRequestDto dto)
        {
            try
            {
                //using var scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMinutes(2), TransactionScopeAsyncFlowOption.Enabled);

                var couponInstance = await _couponIOC._couponInstanceEFCore.QueryAll(d => d.Id == dto.CouponInstanceId).FirstOrDefaultAsync();
                if (couponInstance == null)
                {
                    return GetResult(0, "暂无优惠券信息，请稍后重试！");
                }
                // 1=未使用，2=已使用，3=已过期
                if (couponInstance.Status != 1)
                {
                    return GetResult(0, "该优惠券实例状态不正确，无法核销！");
                }

                // 幂等：同一订单已存在未取消的占用记录则直接返回成功
                var sameOrderExisting = await _couponIOC._couponConsumptionRecordEFCore
                    .QueryAll(r => r.CouponInstanceId == dto.CouponInstanceId
                                   && r.OrderId == dto.OrderId
                                   && !r.IsCanceled)
                    .FirstOrDefaultAsync();
                if (sameOrderExisting != null)
                {
                    return GetResult(1, "已存在核销占用记录，无需重复创建");
                }

                // 并发占用：不同订单对同一券存在未取消占用则拒绝
                var otherOccupyingExists = await _couponIOC._couponConsumptionRecordEFCore
                    .QueryAll(r => r.CouponInstanceId == dto.CouponInstanceId
                                   && r.OrderId != dto.OrderId
                                   && !r.IsCanceled)
                    .AnyAsync();
                if (otherOccupyingExists)
                {
                    return GetResult(0, "该优惠券已被占用处理中，请稍后再试");
                }

                // 仅创建占用记录，不改变实例状态与模板计数
                CouponConsumptionRecord couponConsumptionRecord = new CouponConsumptionRecord()
                {
                    Id = Config.GUID(),
                    CouponInstanceId = dto.CouponInstanceId,
                    OrderId = dto.OrderId,
                    UserId = dto.UserId,
                    ConsumptionTime = DateTime.Now,
                    ConsumptionType = dto.ConsumptionType ?? 3,
                    ConsumptionCodeId = dto.ConsumptionCodeId,
                    //ConsumptionDevice = dto.ConsumptionDevice,
                    ConsumptionIp = Config.GetIp(),
                    IsCanceled = false
                };

                await _couponIOC._couponConsumptionRecordEFCore.AddAsync(couponConsumptionRecord);
                await _couponIOC._couponConsumptionRecordEFCore.SaveChangesAsync();

                return GetResult(1, "创建核销占用记录成功");
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 获取优惠券核销记录列表
        /// </summary>
        /// <returns></returns>
        public async Task<ApiResult> GetCouponConsumptionRecordListAsync()
        {
            string userId = _httpContextAccessor.HttpContext?.GetCurrentUserId() ?? string.Empty;
            var data = await _couponIOC._couponConsumptionRecordEFCore.QueryAll(
                d => (string.IsNullOrEmpty(userId) || d.UserId == userId),
                d => d.ConsumptionTime, false).ToListAsync();
            return GetResult(1, data: data);
        }

        ///// <summary>
        ///// 获取优惠券核销记录详情
        ///// </summary>
        ///// <param name="dto"></param>
        ///// <returns></returns>
        //public async Task<ApiResult> GetCouponConsumptionRecordAsync(CouponConsumptionRecordRequestDto dto)
        //{
        //    var couponConsumptionRecord = await _couponIOC._couponConsumptionRecordEFCore.QueryAll(d => d.Id == dto.Id).SingleOrDefaultAsync();
        //    return GetResult(1, data: couponConsumptionRecord);
        //}

        /// <summary>
        /// 过期优惠券实例处理
        /// </summary>
        /// <param name="userIds"></param>
        /// <param name="templateIds"></param>
        /// <returns></returns>
        public async Task<ApiResult> ExpireCouponInstancesAsync(List<string>? userIds, List<string>? templateIds)
        {
            var now = DateTime.Now;

            var query = _couponIOC._couponInstanceEFCore.QueryAll(d =>
                ((d.Status == 1 || d.Status == 0) && d.AvailableEndTime <= now)
                || (d.Status == 0 && d.ActivationExpirationTime <= now));

            if (userIds != null && userIds.Count > 0)
            {
                query = query.Where(d => userIds.Contains(d.UserId));
            }

            if (templateIds != null && templateIds.Count > 0)
            {
                query = query.Where(d => templateIds.Contains(d.TemplateId));
            }

            var affectedRows = await query.ExecuteUpdateAsync(setters =>
                setters.SetProperty(ci => ci.Status, ci => 3)
                       .SetProperty(ci => ci.LastModificationTime, ci => now));

            return GetResult(1, data: new { affectedRows });
        }

        /// <summary>
        /// 新增抽奖活动
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> AddLotteryActivityAsync(LotteryActivityCreateRequestDto dto)
        {
            try
            {
                using var scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMinutes(2), TransactionScopeAsyncFlowOption.Enabled);

                LotteryActivity lotteryActivity = new LotteryActivity
                {
                    Id = Config.GUID(),
                    Name = dto.Name,
                    Type = dto.Type,
                    ActivityCode = Config.GenerateCode("LA"),
                    Status = dto.Status,
                    StartTime = dto.StartTime,
                    EndTime = dto.EndTime,
                    Channel = dto.Channel,
                    ConfigVersion = dto.ConfigVersion,
                    DailyLimit = dto.DailyLimit,
                    TotalLimit = dto.TotalLimit,
                    CooldownSeconds = dto.CooldownSeconds,
                    EnableStatus = dto.EnableStatus,
                    ExtraProperties = dto.ExtraProperties,
                    ConcurrencyStamp = dto.ConcurrencyStamp,
                    ActivityRules = dto.ActivityRules,

                    RegisterPhone = dto.RegisterPhone,
                    RegisterIdentityCard = dto.RegisterIdentityCard,
                    ExpirationTime = dto.ExpirationTime,

                    IsDeleted = false,
                    CreatorId = _httpContextAccessor.HttpContext?.GetCurrentUserId() ?? string.Empty,
                    CreationTime = DateTime.Now,
                };

                await _couponIOC._lotteryActivityEFCore.AddAsync(lotteryActivity);
                var result = await _couponIOC._lotteryActivityEFCore.SaveChangesAsync();
                if (result == 0)
                {
                    return GetResult(0, "新增抽奖活动失败，请稍后重试！");
                }

                scope.Complete();
                return GetResult(1, data: lotteryActivity.Id);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 获取抽奖活动列表
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> GetLotteryActivityListAsync(LotteryActivityQueryRequestDto dto)
        {
            try
            {
                var data = await _couponIOC._lotteryActivityEFCore.QueryAll(out int total, dto.Page, dto.Limit,
                    d => (string.IsNullOrEmpty(dto.ActivityCode) || d.ActivityCode.Contains(dto.ActivityCode))
                 && (string.IsNullOrEmpty(dto.Name) || d.Name.Contains(dto.Name))
                 && (!dto.Status.HasValue || d.Status == dto.Status.Value)
                 && d.IsDeleted == false,
                    d => d.CreationTime, false).ToListAsync();

                return GetResult(1, data: new
                {
                    total,
                    data
                });
            }
            catch (Exception)
            {

                throw;
            }


        }

        /// <summary>
        /// 获取单个抽奖活动详情
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public async Task<ApiResult> GetLotteryActivityDetailAsync(string Id)
        {
            var data = await _couponIOC._lotteryActivityEFCore.QueryAll(d => d.Id == Id).Select(d => new LotteryActivityQueryResponseDto
            {
                Id = d.Id,
                ActivityCode = d.ActivityCode,
                Name = d.Name,
                Type = d.Type,
                Status = d.Status,
                StartTime = d.StartTime,
                EndTime = d.EndTime,
                Channel = d.Channel,
                ConfigVersion = d.ConfigVersion,
                DailyLimit = d.DailyLimit,
                TotalLimit = d.TotalLimit,
                CooldownSeconds = d.CooldownSeconds,
                EnableStatus = d.EnableStatus,
                ExtraProperties = d.ExtraProperties,
                ConcurrencyStamp = d.ConcurrencyStamp,
                ActivityRules = d.ActivityRules,
                RegisterPhone = d.RegisterPhone,
                RegisterIdentityCard = d.RegisterIdentityCard,
                ExpirationTime = d.ExpirationTime,

                CreationTime = d.CreationTime,
                CreatorId = d.CreatorId,
                LastModificationTime = d.LastModificationTime,
                LastModifierId = d.LastModifierId,
                IsDeleted = d.IsDeleted,
                DeleterId = d.DeleterId,
                DeletionTime = d.DeletionTime,
            }).FirstOrDefaultAsync();

            if (data == null)
            {
                return GetResult(0, "未找到该抽奖活动");
            }

            var lotteryPrizes = await _couponIOC._lotteryPrizeEFCore.QueryAll(d => d.ActivityId == data.Id).ToListAsync();

            foreach (var lotteryPrize in lotteryPrizes)
            {
                var couponTemplate = await _couponIOC._couponTemplateEFCore.QueryAll(d => d.Id == lotteryPrize.TemplateId).FirstOrDefaultAsync();
                data.LotteryPrizes.Add(new LotteryPrizeQueryResponseDto
                {
                    Id = lotteryPrize.Id,
                    ActivityId = lotteryPrize.ActivityId,
                    Ratio = lotteryPrize.Ratio,
                    TemplateId = lotteryPrize.TemplateId,
                    TemplateName = couponTemplate.Name,
                    TemplateType = couponTemplate.CouponType,
                    PosterUrl = couponTemplate.PosterUrl,
                    ExtraProperties = couponTemplate.ExtraProperties,
                    CreationTime = lotteryPrize.CreationTime,
                });
            }

            return GetResult(1, data: data);
        }

        /// <summary>
        /// 编辑单个抽奖活动基本信息
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> UpdateLotteryActivityAsync(LotteryActivityCreateRequestDto dto)
        {
            try
            {
                using var scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMinutes(2), TransactionScopeAsyncFlowOption.Enabled);

                var lotteryActivity = await _couponIOC._lotteryActivityEFCore.QueryAll(d => d.Id == dto.Id).FirstOrDefaultAsync();
                if (lotteryActivity == null)
                {
                    return GetResult(0, "未找到该抽奖活动");
                }
                lotteryActivity.Name = dto.Name;
                lotteryActivity.Type = dto.Type;
                lotteryActivity.StartTime = dto.StartTime;
                lotteryActivity.EndTime = dto.EndTime;
                lotteryActivity.Channel = dto.Channel;
                lotteryActivity.ConfigVersion = dto.ConfigVersion;
                lotteryActivity.DailyLimit = dto.DailyLimit;
                lotteryActivity.TotalLimit = dto.TotalLimit;
                lotteryActivity.CooldownSeconds = dto.CooldownSeconds;
                lotteryActivity.EnableStatus = dto.EnableStatus;
                lotteryActivity.ExtraProperties = dto.ExtraProperties;
                lotteryActivity.ConcurrencyStamp = dto.ConcurrencyStamp;
                lotteryActivity.ActivityRules = dto.ActivityRules;
                lotteryActivity.RegisterPhone = dto.RegisterPhone;
                lotteryActivity.RegisterIdentityCard = dto.RegisterIdentityCard;
                lotteryActivity.ExpirationTime = dto.ExpirationTime;

                lotteryActivity.LastModificationTime = DateTime.Now;
                lotteryActivity.LastModifierId = _httpContextAccessor.HttpContext?.GetCurrentUserId() ?? string.Empty;

                await _couponIOC._lotteryActivityEFCore.UpdateAsync(lotteryActivity);
                var result = await _couponIOC._lotteryActivityEFCore.SaveChangesAsync();
                if (result == 0)
                {
                    return GetResult(0, "编辑抽奖活动失败，请稍后重试！");
                }

                scope.Complete();
                return GetResult(1);
            }
            catch (Exception)
            {

                throw;
            }

        }

        /// <summary>
        /// 编辑抽奖活动状态
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> UpdateLotteryActivityStatusAsync(LotteryActivityUpdateRequestDto dto)
        {
            try
            {
                using var scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMinutes(2), TransactionScopeAsyncFlowOption.Enabled);

                var lotteryActivity = await _couponIOC._lotteryActivityEFCore.QueryAll(d => d.Id == dto.Id).FirstOrDefaultAsync();
                if (lotteryActivity == null)
                {
                    return GetResult(0, "未找到该抽奖活动");
                }

                lotteryActivity.Status = dto.Status;
                lotteryActivity.LastModificationTime = DateTime.Now;
                lotteryActivity.LastModifierId = _httpContextAccessor.HttpContext?.GetCurrentUserId() ?? string.Empty;

                await _couponIOC._lotteryActivityEFCore.UpdateAsync(lotteryActivity);
                var result = await _couponIOC._lotteryActivityEFCore.SaveChangesAsync();
                if (result == 0)
                {
                    return GetResult(0, "编辑抽奖活动失败，请稍后重试！");
                }

                scope.Complete();
                return GetResult(1);
            }
            catch (Exception)
            {

                throw;
            }
        }

        /// <summary>
        /// 删除抽奖活动
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public async Task<ApiResult> DeleteLotteryActivityAsync(string Id)
        {
            try
            {
                using var scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMinutes(2), TransactionScopeAsyncFlowOption.Enabled);

                var lotteryActivity = await _couponIOC._lotteryActivityEFCore.QueryAll(d => d.Id == Id).FirstOrDefaultAsync();
                if (lotteryActivity == null)
                {
                    return GetResult(0, "未找到该抽奖活动");
                }

                lotteryActivity.IsDeleted = true;
                lotteryActivity.DeletionTime = DateTime.Now;
                lotteryActivity.DeleterId = _httpContextAccessor.HttpContext?.GetCurrentUserId() ?? string.Empty;

                await _couponIOC._lotteryActivityEFCore.UpdateAsync(lotteryActivity);
                var result = await _couponIOC._lotteryActivityEFCore.SaveChangesAsync();
                if (result == 0)
                {
                    return GetResult(0, "编辑抽奖活动失败，请稍后重试！");
                }

                scope.Complete();
                return GetResult(1);

            }
            catch (Exception)
            {

                throw;
            }
        }

        /// <summary>
        /// 新增抽奖奖品（批量）
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> AddLotteryPrizesAsync(LotteryPrizeCreateRequestDto dto)
        {
            try
            {
                using var scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMinutes(2), TransactionScopeAsyncFlowOption.Enabled);

                var lotteryActivity = await _couponIOC._lotteryActivityEFCore.QueryAll(d => d.Id == dto.ActivityId).FirstOrDefaultAsync();
                if (lotteryActivity == null)
                {
                    return GetResult(0, "未找到该抽奖活动");
                }

                List<LotteryPrize> lotteryPrizes = new List<LotteryPrize>();
                foreach (var lotteryPrize in dto.LotteryPrizes)
                {
                    LotteryPrize lottery = new LotteryPrize()
                    {
                        Id = Config.GUID(),
                        ActivityId = dto.ActivityId,
                        TemplateId = lotteryPrize.TemplateId,
                        Ratio = lotteryPrize.Ratio,
                        ExtraProperties = lotteryPrize.ExtraProperties,
                        Enabled = true,
                        Stock = 0,
                        CreationTime = DateTime.Now
                    };
                    lotteryPrizes.Add(lottery);
                }

                await _couponIOC._lotteryPrizeEFCore.AddRangeAsync(lotteryPrizes);
                var result = await _couponIOC._lotteryPrizeEFCore.SaveChangesAsync();

                if (result == 0)
                {
                    return GetResult(0, "新增抽奖奖品失败，请稍后重试！");
                }

                scope.Complete();
                return GetResult(1);
            }
            catch (Exception)
            {

                throw;
            }
        }

        /// <summary>
        /// 编辑抽奖奖品（批量）
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> UpdateLotteryPrizesAsync(LotteryPrizeCreateRequestDto dto)
        {
            try
            {
                using var scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMinutes(2), TransactionScopeAsyncFlowOption.Enabled);

                var lotteryActivity = await _couponIOC._lotteryActivityEFCore.QueryAll(d => d.Id == dto.ActivityId).FirstOrDefaultAsync();
                if (lotteryActivity == null)
                {
                    return GetResult(0, "未找到该抽奖活动");
                }

                var lotteryPrizeList = await _couponIOC._lotteryPrizeEFCore.QueryAll(d => d.ActivityId == dto.ActivityId).ToListAsync();

                if (lotteryPrizeList.Count > 0)
                {
                    await _couponIOC._lotteryPrizeEFCore.DeleteRangeAsync(lotteryPrizeList);
                    var deleteResult = await _couponIOC._lotteryPrizeEFCore.SaveChangesAsync();

                    if (deleteResult == 0)
                    {
                        return GetResult(0, "清理抽奖奖品失败，请稍后重试！");
                    }
                }

                List<LotteryPrize> lotteryPrizes = new List<LotteryPrize>();
                foreach (var lotteryPrize in dto.LotteryPrizes)
                {
                    LotteryPrize lottery = new LotteryPrize()
                    {
                        Id = Config.GUID(),
                        ActivityId = dto.ActivityId,
                        TemplateId = lotteryPrize.TemplateId,
                        Ratio = lotteryPrize.Ratio,
                        ExtraProperties = lotteryPrize.ExtraProperties,
                        CreationTime = DateTime.Now
                    };
                    lotteryPrizes.Add(lottery);
                }

                await _couponIOC._lotteryPrizeEFCore.AddRangeAsync(lotteryPrizes);
                var result = await _couponIOC._lotteryPrizeEFCore.SaveChangesAsync();

                if (result == 0)
                {
                    return GetResult(0, "新增抽奖奖品失败，请稍后重试！");
                }

                scope.Complete();
                return GetResult(1);
            }
            catch (Exception)
            {

                throw;
            }
        }

        /// <summary>
        /// 点击抽奖
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> LotteryDrawAsync(LotteryDrawRequestDto dto)
        {
            try
            {
                //using var scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMinutes(2), TransactionScopeAsyncFlowOption.Enabled);

                // 当前用户
                string userId = _httpContextAccessor.HttpContext?.GetCurrentUserId() ?? string.Empty;
                if (string.IsNullOrEmpty(userId)) return GetResult(message: "获取不到用户信息，请重新登录");

                var userMini = await _couponIOC._sysUserMiniProgramEFCore.QueryAll(d => d.Id == userId).FirstOrDefaultAsync();
                if (userMini == null) return GetResult(message: "获取不到用户信息，请重新登录");

                // 幂等：重复请求直接返回
                var already = await _couponIOC._lotteryClaimRecordEFCore.QueryAll(r => r.ActivityId == dto.ActivityId && r.UserId == userId && r.RequestId == dto.RequestId).FirstOrDefaultAsync();
                if (already != null)
                {
                    return GetResult(1, data: new { already.Status, already.Issued, already.AwardId, already.CouponId, already.TargetSlot, already.TargetCell, already.Message });
                }

                // 活动校验
                var lotteryActivity = await _couponIOC._lotteryActivityEFCore.QueryAll(d => d.Id == dto.ActivityId).FirstOrDefaultAsync();
                if (lotteryActivity == null)
                    return GetResult(0, "未找到该抽奖活动");

                var now = DateTime.Now;

                if (lotteryActivity.EnableStatus != 1 || lotteryActivity.Status != 2) return GetResult(0, "活动未启用或未上架");

                if (now < lotteryActivity.StartTime || now > lotteryActivity.EndTime) return GetResult(0, "活动不在有效期内");

                if (!string.IsNullOrEmpty(dto.ConfigVersion) && lotteryActivity.ConfigVersion != dto.ConfigVersion) return GetResult(0, "版本不匹配");

                // 限流：当日/总次数与冷却
                var unionIds = new List<string>();
                if (!string.IsNullOrEmpty(userMini.UnionId))
                    unionIds = await _couponIOC._sysUserMiniProgramEFCore.QueryAll(d => d.UnionId == userMini.UnionId).Select(d => d.Id).ToListAsync();
                else unionIds.Add(userId);

                var todayStart = now.Date;
                var todayEnd = now.Date.AddDays(1);
                var dailyCount = await _couponIOC._lotteryClaimRecordEFCore
                    .QueryAll(r => r.ActivityId == dto.ActivityId && unionIds.Contains(r.UserId) && r.CreationTime >= todayStart && r.CreationTime < todayEnd)
                    .CountAsync();
                var totalCount = await _couponIOC._lotteryClaimRecordEFCore
                    .QueryAll(r => r.ActivityId == dto.ActivityId && unionIds.Contains(r.UserId))
                    .CountAsync();
                var lastRecordTime = await _couponIOC._lotteryClaimRecordEFCore
                    .QueryAll(r => r.ActivityId == dto.ActivityId && unionIds.Contains(r.UserId))
                    .OrderByDescending(r => r.CreationTime)
                    .Select(r => r.CreationTime)
                    .FirstOrDefaultAsync();

                if (lotteryActivity.DailyLimit > 0 && dailyCount >= lotteryActivity.DailyLimit) return GetResult(0, "超过每日参与次数上限");
                if (lotteryActivity.TotalLimit > 0 && totalCount >= lotteryActivity.TotalLimit) return GetResult(0, "超过活动期间总参与次数上限");
                if (lotteryActivity.CooldownSeconds > 0 && lastRecordTime != default && (now - lastRecordTime).TotalSeconds < lotteryActivity.CooldownSeconds) return GetResult(0, "冷却时间未到");

                // 奖品列表（仅有效奖品参与抽取）
                //var prizeList = await _couponIOC._lotteryPrizeEFCore.QueryAll(p => p.ActivityId == dto.ActivityId && (p.Enabled == true || p.Enabled == null)).OrderBy(p => p.Sort ?? 0).ToListAsync();

                // 按库存过滤（Stock==null 表示不限；Stock>0 表示可抽）
                //var candidatePrizes = prizeList.Where(p => p.Stock == null || p.Stock > 0).ToList();

                var lotteryPrizes = await _couponIOC._lotteryPrizeEFCore.QueryAll(d => d.ActivityId == dto.ActivityId).ToListAsync();
                if (lotteryPrizes == null || lotteryPrizes.Count == 0)
                {
                    return GetResult(0, "该抽奖活动暂无奖品，请联系管理员！");
                }
                // 计算中奖结果
                // 加权随机
                int hitIndex = -1;
                try
                {
                    hitIndex = WeightedDraw(lotteryPrizes);
                }
                catch (ArgumentException ex)
                {
                    // 奖品比例配置错误
                    return GetResult(0, "奖品比例配置错误，当次抽奖机会不计入使用，请联系相关负责人处理！");
                }

                // 谢谢参与
                if (hitIndex < 0)
                {
                    await AddLotteryClaimRecordAsync(new LotteryClaimRecordCreateRequestDto()
                    {
                        RequestId = dto.RequestId,
                        ActivityId = dto.ActivityId,
                        UserId = userId,
                        AwardId = null,
                        Issued = false,
                        Status = 0,
                        CouponId = null,
                        TemplateId = null,
                        Message = "谢谢参与",
                        Channel = dto.Channel,
                        TargetSlot = null,
                        TargetCell = null,
                        ConfigVersion = dto.ConfigVersion,
                        ExtraProperties = null,
                    });

                    //scope.Complete();
                    return GetResult(1, data: new { AwardId = (string?)null, Issued = false, message = "谢谢参与" });
                }

                // 命中奖品
                var award = lotteryPrizes[hitIndex];

                var result = await UserGetCouponAsync(new CouponInstanceCreateRequestDto()
                {
                    TemplateIds = new List<string> { award.TemplateId },
                    RecommendMerchantId = null,
                    ActivityId = dto.ActivityId,
                    ActivationExpirationTime = lotteryActivity.ExpirationTime == 0 ? null : now.AddSeconds((double)lotteryActivity.ExpirationTime),
                });

                if (result.code == ApiCode.ok)
                {
                    var resultData = result.data as GetCouponsResponseDto;
                    var couponId = resultData.Ids[0];

                    // 根据活动配置与用户实名信息，判定是否自动激活中奖券
                    bool needPhone = lotteryActivity.RegisterPhone;
                    bool needIdCard = lotteryActivity.RegisterIdentityCard;

                    bool canActivate = false;

                    if (!needPhone && !needIdCard)
                    {
                        // 活动不要求手机号/身份证，直接激活
                        canActivate = true;
                    }
                    else
                    {
                        // 需要手机号或身份证时，检查当前用户实名信息是否已填写
                        var userResult = await _userService.CheckRealNameUserInformationAsync(userId);
                        var userData = userResult.data as CheckRealNameUserInformationResponseDto;

                        bool hasPhone = userData?.RegisterPhone == true;
                        bool hasIdCard = userData?.RegisterIdentityCard == true;

                        if (needPhone && needIdCard)
                        {
                            // 同时要求：手机号 + 身份证
                            canActivate = hasPhone && hasIdCard;
                        }
                        else if (needPhone)
                        {
                            // 仅要求：手机号
                            canActivate = hasPhone;
                        }
                        else
                        {
                            // 仅要求：身份证
                            canActivate = hasIdCard;
                        }
                    }

                    if (canActivate)
                    {
                        // 自动激活优惠券实例
                        await ActivateCouponInstanceAsync(new LotteryPrizeActivateRequestDto { CouponInstanceId = couponId ,ActivityId = dto.ActivityId });
                    }

                    await AddLotteryClaimRecordAsync(new LotteryClaimRecordCreateRequestDto()
                    {
                        RequestId = dto.RequestId,
                        ActivityId = dto.ActivityId,
                        UserId = userId,
                        AwardId = award.Id,
                        Issued = true,
                        Status = canActivate ? 1 : 2,// 1=成功发放，2=未实名信息待激活
                        CouponId = couponId,
                        TemplateId = award.TemplateId,
                        Message = "恭喜获奖",
                        Channel = dto.Channel,
                        TargetSlot = null,
                        TargetCell = null,
                        ConfigVersion = dto.ConfigVersion,
                        ExtraProperties = null,
                    });

                    //scope.Complete();
                    return GetResult(1, data: new { AwardId = award.Id, Issued = true, message = "恭喜获奖" });
                }
                else
                {
                    await AddLotteryClaimRecordAsync(new LotteryClaimRecordCreateRequestDto()
                    {
                        RequestId = dto.RequestId,
                        ActivityId = dto.ActivityId,
                        UserId = userId,
                        AwardId = null,
                        Issued = false,
                        Status = 0,
                        CouponId = null,
                        TemplateId = null,
                        Message = "无法领取该奖品",
                        Channel = dto.Channel,
                        TargetSlot = null,
                        TargetCell = null,
                        ConfigVersion = dto.ConfigVersion,
                        ExtraProperties = null,
                    });

                    //scope.Complete();
                    return GetResult(1, data: new { AwardId = (string?)null, Issued = false, message = "无法领取该奖品" });
                }

                //scope.Complete();
                //return GetResult(1);
            }
            catch (Exception)
            {

                throw;
            }
        }

        /// <summary>
        /// 根据奖品比例进行加权随机，返回命中奖品索引；若未命中有效奖品，返回 -1 表示“谢谢参与”
        /// </summary>
        /// <param name="prizes">可参与的奖品列表（已过滤 Enabled/库存等）</param>
        /// <returns>命中奖品索引；-1 表示“谢谢参与”</returns>
        /// <exception cref="ArgumentException">当比例总和>1或比例小于最小阈值时抛出</exception>
        public int WeightedDraw(IReadOnlyList<LotteryPrize> prizes)
        {
            // 业务约束：最大100%（1.0），最小0.01%（0.0001）
            const decimal minRatio = 0.0001m;

            decimal sum = 0m;
            foreach (var p in prizes)
            {
                if (p.Ratio < minRatio) throw new ArgumentException("存在奖品比例低于最小阈值0.01%");
                sum += p.Ratio;
            }
            if (sum > 1m) throw new ArgumentException("奖品比例总和不能超过1");

            // 安全随机数生成 [0,1)
            // 使用 1,000,000 精度，可满足 0.01% 粒度
            int r = System.Security.Cryptography.RandomNumberGenerator.GetInt32(0, 1_000_000);
            decimal x = r / 1_000_000m;

            decimal acc = 0m;
            for (int i = 0; i < prizes.Count; i++)
            {
                acc += prizes[i].Ratio;
                if (x < acc) return i;
            }
            // 未命中任何有效奖品，则“谢谢参与”
            return -1;
        }

        /// <summary>
        /// 新增抽奖领取记录
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> AddLotteryClaimRecordAsync(LotteryClaimRecordCreateRequestDto dto)
        {
            try
            {
                //using var scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMinutes(2), TransactionScopeAsyncFlowOption.Enabled);

                var userId = dto.UserId ?? _httpContextAccessor.HttpContext?.GetCurrentUserId() ?? string.Empty;
                LotteryClaimRecord lotteryClaimRecord = new LotteryClaimRecord
                {
                    Id = Config.GUID(),
                    RequestId = dto.RequestId,
                    ActivityId = dto.ActivityId,
                    UserId = userId,
                    AwardId = dto.AwardId,
                    Issued = dto.Issued,
                    Status = dto.Status,
                    CouponId = dto.CouponId,
                    TemplateId = dto.TemplateId,
                    Message = dto.Message,
                    Channel = dto.Channel,
                    TargetSlot = dto.TargetSlot,
                    TargetCell = dto.TargetCell,
                    ConfigVersion = dto.ConfigVersion,
                    ExtraProperties = dto.ExtraProperties,
                    CreationTime = DateTime.Now,
                };

                await _couponIOC._lotteryClaimRecordEFCore.AddAsync(lotteryClaimRecord);
                var result = await _couponIOC._lotteryClaimRecordEFCore.SaveChangesAsync();
                if (result == 0)
                {
                    return GetResult(0, "新增抽奖领取记录失败，请稍后重试！");
                }
                return GetResult(1);
            }
            catch (Exception)
            {

                throw;
            }
        }


        /// <summary>
        /// 获取抽奖领取记录列表
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> GetLotteryClaimRecordListAsync(LotteryClaimRecordQueryRequestDto dto)
        {
            try
            {
                var userId = dto.UserId ?? _httpContextAccessor.HttpContext?.GetCurrentUserId() ?? string.Empty;

                var data = await _couponIOC._lotteryClaimRecordEFCore.QueryAll(out int total, dto.Page, dto.Limit,
                    d => (string.IsNullOrEmpty(dto.ActivityId) || d.ActivityId == dto.ActivityId)
                    && (string.IsNullOrEmpty(userId) || d.UserId == userId),
                    d => d.CreationTime, false).Select(d => new LotteryClaimRecordQueryResponseDto()
                    {
                        Id = d.Id,
                        RequestId = d.RequestId,
                        ActivityId = d.ActivityId,
                        UserId = d.UserId,
                        AwardId = d.AwardId,
                        Issued = d.Issued,
                        Status = d.Status,
                        CouponId = d.CouponId,
                        TemplateId = d.TemplateId,
                        Message = d.Message,
                        Channel = d.Channel,
                        TargetSlot = d.TargetSlot,
                        TargetCell = d.TargetCell,
                        ConfigVersion = d.ConfigVersion,
                        CreationTime = d.CreationTime,
                        ExtraProperties = d.ExtraProperties,
                    }).ToListAsync();

                foreach (var lotteryClaimRecord in data)
                {
                    var couponTemplate = await _couponIOC._couponTemplateEFCore.QueryAll(t => t.Id == lotteryClaimRecord.TemplateId).FirstOrDefaultAsync();
                    if (couponTemplate != null)
                    {
                        lotteryClaimRecord.TemplateName = couponTemplate.Name;
                        lotteryClaimRecord.PosterUrl = couponTemplate.PosterUrl;
                        lotteryClaimRecord.ExtraProperties = couponTemplate.ExtraProperties;
                    }
                }

                return GetResult(1, data: new
                {
                    total,
                    data
                });
            }
            catch (Exception)
            {

                throw;
            }
        }

        /// <summary>
        /// 获取用户奖品列表
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> GetUserLotteryPrizesListAsync(LotteryClaimRecordQueryRequestDto dto)
        {
            try
            {
                var userId = dto.UserId ?? _httpContextAccessor.HttpContext?.GetCurrentUserId() ?? string.Empty;

                //过期优惠券实例
                await ExpireCouponInstancesAsync(userIds: new List<string> { userId }, null);
                //过期用户抽奖奖品
                await ExpireUserLotteryPrizesAsync(userId);

                var data = await _couponIOC._lotteryClaimRecordEFCore.QueryAll(out int total, dto.Page, dto.Limit,
                    d => (string.IsNullOrEmpty(dto.ActivityId) || d.ActivityId == dto.ActivityId)
                    && (string.IsNullOrEmpty(userId) || d.UserId == userId)
                    && !string.IsNullOrEmpty(d.CouponId),
                    d => d.CreationTime, false).Select(d => new LotteryClaimRecordQueryResponseDto()
                    {
                        Id = d.Id,
                        RequestId = d.RequestId,
                        ActivityId = d.ActivityId,
                        UserId = d.UserId,
                        AwardId = d.AwardId,
                        Issued = d.Issued,
                        Status = d.Status,
                        CouponId = d.CouponId,
                        TemplateId = d.TemplateId,
                        Message = d.Message,
                        Channel = d.Channel,
                        TargetSlot = d.TargetSlot,
                        TargetCell = d.TargetCell,
                        ConfigVersion = d.ConfigVersion,
                        CreationTime = d.CreationTime,
                        ExtraProperties = d.ExtraProperties,
                    }).ToListAsync();

                foreach (var lotteryClaimRecord in data)
                {
                    var couponTemplate = await _couponIOC._couponTemplateEFCore.QueryAll(t => t.Id == lotteryClaimRecord.TemplateId).FirstOrDefaultAsync();
                    if (couponTemplate != null)
                    {
                        lotteryClaimRecord.TemplateName = couponTemplate.Name;
                        lotteryClaimRecord.PosterUrl = couponTemplate.PosterUrl;
                        lotteryClaimRecord.ExtraProperties = couponTemplate.ExtraProperties;
                    }
                }

                return GetResult(1, data: new
                {
                    total,
                    data
                });
            }
            catch (Exception)
            {

                throw;
            }
        }

        /// <summary>
        /// 过期用户抽奖奖品
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <returns>返回处理结果，data 中携带受影响的记录数</returns>
        /// <exception cref="Exception">数据库访问或更新异常</exception>
        public async Task<ApiResult> ExpireUserLotteryPrizesAsync(string userId)
        {
            try
            {
                // 查询该用户的抽奖记录（只处理有券实例且当前状态不是失效的记录）
                var claimList = await _couponIOC._lotteryClaimRecordEFCore
                    .QueryAll(d => d.UserId == userId && !string.IsNullOrEmpty(d.CouponId) && d.Status != 0)
                    .ToListAsync();

                if (claimList.Count == 0)
                {
                    return GetResult(1, data: new { affectedRows = 0 });
                }

                // 取出关联的券实例ID并查询已过期的券实例（状态=3）
                var couponIds = claimList
                    .Where(c => !string.IsNullOrEmpty(c.CouponId))
                    .Select(c => c.CouponId!)
                    .Distinct()
                    .ToList();

                var expiredCouponIds = await _couponIOC._couponInstanceEFCore
                    .QueryAll(ci => couponIds.Contains(ci.Id) && ci.Status == 3)
                    .Select(ci => ci.Id)
                    .ToListAsync();

                if (expiredCouponIds.Count == 0)
                {
                    return GetResult(1, data: new { affectedRows = 0 });
                }

                // 将对应的抽奖记录状态改为 0=失效
                var toUpdate = claimList
                    .Where(c => c.CouponId != null && expiredCouponIds.Contains(c.CouponId!) && c.Status != 0)
                    .ToList();

                foreach (var claim in toUpdate)
                {
                    claim.Status = 0;
                    claim.Message = "奖品对应优惠券已过期";
                }

                if (toUpdate.Count > 0)
                {
                    await _couponIOC._lotteryClaimRecordEFCore.UpdateRangeAsync(toUpdate);
                    var affected = await _couponIOC._lotteryClaimRecordEFCore.SaveChangesAsync();
                    return GetResult(1, data: new { affectedRows = affected });
                }

                return GetResult(1, data: new { affectedRows = 0 });
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 激活优惠券实例
        /// </summary>
        /// <returns></returns>
        public async Task<ApiResult> ActivateCouponInstanceAsync(LotteryPrizeActivateRequestDto dto)
        {
            try
            {
                // 自动激活：将券实例状态由未激活(0)置为未使用(1)，避免过期
                var nowTime = DateTime.Now;
                var affected = await _couponIOC._couponInstanceEFCore
                    .QueryAll(ci => ci.Id == dto.CouponInstanceId
                                    && ci.Status == 0
                                    && (ci.ActivationExpirationTime == null || ci.ActivationExpirationTime > nowTime))
                    .ExecuteUpdateAsync(setters => setters
                        .SetProperty(ci => ci.Status, 1)
                        .SetProperty(ci => ci.LastModificationTime, nowTime));

                var lotteryClaimRecord = await _couponIOC._lotteryClaimRecordEFCore.QueryAll(d => d.ActivityId == dto.ActivityId && d.CouponId == dto.CouponInstanceId).FirstOrDefaultAsync();
                if (lotteryClaimRecord != null)
                {
                    lotteryClaimRecord.Status = 0;
                    lotteryClaimRecord.Message = "奖品对应优惠券已过期";
                    
                    await _couponIOC._lotteryClaimRecordEFCore.UpdateAsync(lotteryClaimRecord);
                    var result = await _couponIOC._lotteryClaimRecordEFCore.SaveChangesAsync();
                }
                return GetResult(affected);
                // 受影响行数为 0 表示券可能已过期或已激活，保持获奖结果不变
            }
            catch (Exception)
            {

                throw;
            }
        }
    }
}
